﻿/********************************************************************************
** 作者： Mick
** 邮箱： zheng_jinfan@126.com
** 主页： http://www.zhengjinfan.cn
** 创始时间：2016-2-17
** 描述：
**      [辅助类]压缩-解压缩辅助类
*********************************************************************************/
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Win32;

namespace BestEasyCMS.Framework.Util
{
    /// <summary>
    ///SharpZipLib 的摘要说明 
    ///前台调用示例：
    ///SharpZipLib szl = new SharpZipLib();
    ///string ServerDir= Server.MapPath("../WordTemplate")+"//";
    ///szl.ZipFile("test.txt*test.docx*测试.zip",ServerDir);
    ///szl.UnZipFile("测试.zip", ServerDir);
    ///Response.Redirect("../WordTemplate/测试.zip");//下载压缩文件
    ///
    /// </summary>
    public class SharpZipHelper
    {
        #region 将带路径的文件转为文件名
        /// <summary>
        /// 将带路径的文件转为文件名
        /// </summary>
        /// <param name="s">绝对路径</param>
        /// <param name="serverDir">父级文件夹绝对路径</param>
        /// <returns>string</returns>
        public string ShortDir(string s, string serverDir)
        {
            var d = s.Replace(serverDir, "");
            return d;
        }
        #endregion

        #region 压缩文件
        /// <summary>
        /// 压缩文件
        /// </summary>
        /// <param name="p">文件列表+压缩文件名（如“test.txt*test.docx*000.zip”）</param>
        /// <param name="serverDir">父级文件夹绝对路径</param>
        public void ZipFile(string p, string serverDir)
        {
            var tmp = p.Split(new[] { '*' });  //分离文件列表
            if (tmp[tmp.Length - 1] == "") return;
            var u = new ZipOutputStream(File.Create(serverDir + tmp[tmp.Length - 1])); //新建压缩文件流 “ZipOutputStream”
            for (var i = 0; i < tmp.Length - 1; i++)
            {
                if (tmp[i] != "")  //分离出来的文件名不为空
                {
                    AddZipEntry(tmp[i], u, out u, serverDir); //向压缩文件流加入内容
                }
            }
            u.Finish(); // 结束压缩
            u.Close();
        }

        /// <summary>
        /// 添加压缩项目
        /// </summary>
        /// <param name="p">需压缩的文件或文件夹</param>
        /// <param name="u">现有的源ZipOutputStream</param>
        /// <param name="j">已添加“ZipEntry”的“ZipOutputStream”</param>
        /// <param name="serverDir">父级文件夹绝对路径</param>
        public void AddZipEntry(string p, ZipOutputStream u, out ZipOutputStream j, string serverDir)
        {
            var s = serverDir + p;

            if (Directory.Exists(s))  //文件夹的处理
            {
                var di = new DirectoryInfo(s);

                if (di.GetDirectories().Length <= 0)   //没有子目录
                {
                    var z = new ZipEntry(p + "//"); //末尾“//”用于文件夹的标记
                    u.PutNextEntry(z);
                }


                foreach (var tem in di.GetDirectories())  //获取子目录
                {
                    var z = new ZipEntry(ShortDir(tem.FullName, serverDir) + "//"); //末尾“//”用于文件夹的标记
                    u.PutNextEntry(z);    //此句不可少，否则空目录不会被添加
                    s = ShortDir(tem.FullName, serverDir);
                    AddZipEntry(s, u, out u, serverDir);       //递归
                }
                foreach (var temp in di.GetFiles())  //获取此目录的文件
                {
                    s = ShortDir(temp.FullName, serverDir);
                    AddZipEntry(s, u, out u, serverDir);      //递归
                }
            }
            else if (File.Exists(s))  //文件的处理
            {
                u.SetLevel(9);      //压缩等级
                var f = File.OpenRead(s);
                var b = new byte[f.Length];
                f.Read(b, 0, b.Length);          //将文件流加入缓冲字节中
                var z = new ZipEntry(ShortDir(s, serverDir));
                u.PutNextEntry(z);             //为压缩文件流提供一个容器
                u.Write(b, 0, b.Length);  //写入字节
                f.Close();
            }
            j = u;    //返回已添加数据的“ZipOutputStream”
        }
        #endregion

        #region 解压缩
        /// <summary>
        /// 解压缩
        /// </summary>
        /// <param name="p">需解压缩的文件或文件夹</param>
        /// <param name="serverDir">父级文件夹绝对路径</param>
        public void UnZipFile(string p, string serverDir)
        {
            var unTmp = p.Split(new[] { '*' });
            var i2 = 0;  //防止名称冲突的参数
            for (var j = 0; j < unTmp.Length; j++)
            {
                if (unTmp[j] != "")
                {
                    //var unTime = unTmp[j].Substring(0, unTmp[j].Length - 4) + "_" + i2;
                    var unDir = serverDir; //+ "Unzip-" + un_time;
                    Directory.CreateDirectory(unDir);    //创建以解压时间为名称的文件夹
                    var f = new ZipInputStream(File.OpenRead(unTmp[j])); //读取压缩文件，并用此文件流新建 “ZipInputStream”对象
                    try
                    {
                    A: var zp = f.GetNextEntry();   //获取解压文件流中的项目。 

                        while (zp != null)
                        {
                            if (zp.Name.IndexOf("/", StringComparison.Ordinal) >= 0) //获取文件的目录信息
                            {
                                var tmp1 = zp.Name.LastIndexOf("/", StringComparison.Ordinal);
                                var unTmp2 = zp.Name.Substring(0, tmp1);
                                Directory.CreateDirectory(unDir + "/" + unTmp2 + "/"); //必须先创建目录，否则解压失败 --- （A） 关系到下面的步骤（B）
                            }
                            if (!zp.IsDirectory && zp.Crc != 00000000L) //此“ZipEntry”不是“标记文件”
                            {
                                var i = 2048;
                                var b = new byte[i];  //每次缓冲 2048 字节
                                var s = File.Create(unDir + "//" + zp.Name); //（B)-新建文件流
                                while (true) //持续读取字节，直到一个“ZipEntry”字节读完
                                {
                                    i = f.Read(b, 0, b.Length); //读取“ZipEntry”中的字节
                                    if (i > 0)
                                    {
                                        s.Write(b, 0, i); //将字节写入新建的文件流
                                    }
                                    else
                                    {
                                        break; //读取的字节为 0 ，跳出循环
                                    }
                                }
                                s.Close();
                            }
                            goto A; //进入下一个“ZipEntry”
                        }
                        f.Close();
                        i2++;
                    }
                    catch (Exception ex)
                    {

                        f.Close();//上传的文件若无法解压必须释放资源
                        throw new Exception(ex.ToString());
                    }
                }
            }
        }
        #endregion


        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="dirPath">压缩文件夹的路径</param>
        /// <param name="fileName">生成的zip文件路径</param>
        /// <param name="level">压缩级别 0 - 9 0是存储级别 9是最大压缩</param>
        /// <param name="bufferSize">读取文件的缓冲区大小</param>
        public void CompressDirectory(string dirPath, string fileName, int level, int bufferSize)
        {
            var buffer = new byte[bufferSize];
            var s = new ZipOutputStream(File.Create(fileName));
            s.SetLevel(level);
            CompressDirectory(dirPath, dirPath, s, buffer);
            s.Finish();
            s.Close();
        }
        /// <summary>
        /// 压缩文件夹
        /// </summary>
        /// <param name="root">压缩文件夹路径</param>
        /// <param name="path">压缩文件夹内当前要压缩的文件夹路径</param>
        /// <param name="s"></param>
        /// <param name="buffer">读取文件的缓冲区大小</param>
        private void CompressDirectory(string root, string path, ZipOutputStream s, byte[] buffer)
        {
            root = root.TrimEnd('\\') + "\\";
            var fileNames = Directory.GetFiles(path);
            var dirNames = Directory.GetDirectories(path);
            var relativePath = path.Replace(root, "");
            if (relativePath != "")
            {
                relativePath = relativePath.Replace("\\", "/") + "/";
            }
            foreach (var file in fileNames)
            {

                var entry = new ZipEntry(relativePath + Path.GetFileName(file)) { DateTime = DateTime.Now };
                s.PutNextEntry(entry);
                using (var fs = File.OpenRead(file))
                {
                    int sourceBytes;
                    do
                    {
                        sourceBytes = fs.Read(buffer, 0, buffer.Length);
                        s.Write(buffer, 0, sourceBytes);
                    } while (sourceBytes > 0);
                }
            }
            foreach (var dirName in dirNames)
            {
                var relativeDirPath = dirName.Replace(root, "");
                var entry = new ZipEntry(relativeDirPath.Replace("\\", "/") + "/");
                s.PutNextEntry(entry);
                CompressDirectory(root, dirName, s, buffer);
            }
        }

    }

    public class SharpZip
    {
        /// <summary>
        /// 压缩
        /// </summary> 
        /// <param name="filename"> 压缩后的文件名(包含物理路径)</param>
        /// <param name="directory">待压缩的文件夹(包含物理路径)</param>
        public static void PackFiles(string filename, string directory)
        {
            var fz = new FastZip
            {
                CreateEmptyDirectories = true
            };
            fz.CreateZip(filename, directory, true, "");
        }

        /// <summary>
        /// 解压缩
        /// </summary>
        /// <param name="file">待解压文件名(包含物理路径)</param>
        /// <param name="dir"> 解压到哪个目录中(包含物理路径)</param>
        public static bool UnpackFiles(string file, string dir)
        {
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            var s = new ZipInputStream(File.OpenRead(file));
            ZipEntry theEntry;
            while ((theEntry = s.GetNextEntry()) != null)
            {
                var directoryName = Path.GetDirectoryName(theEntry.Name);
                var fileName = Path.GetFileName(theEntry.Name);
                if (directoryName != String.Empty)
                {
                    Directory.CreateDirectory(dir + directoryName);
                }
                if (fileName != String.Empty)
                {
                    var streamWriter = File.Create(dir + theEntry.Name);
                    var data = new byte[2048];
                    while (true)
                    {
                        var size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                    streamWriter.Close();
                }
            }
            s.Close();
            return true;
        }
    }

    public class ClassZip
    {
        #region 私有方法
        /// <summary>
        /// 递归压缩文件夹方法
        /// </summary>
        private static bool ZipFileDictory(string folderToZip, ZipOutputStream s, string parentFolderName)
        {
            var res = true;
            ZipEntry entry = null;
            FileStream fs = null;
            var crc = new Crc32();
            try
            {
                entry = new ZipEntry(Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/"));
                s.PutNextEntry(entry);
                s.Flush();
                var filenames = Directory.GetFiles(folderToZip);
                foreach (var file in filenames)
                {
                    fs = File.OpenRead(file);
                    var buffer = new byte[fs.Length];
                    fs.Read(buffer, 0, buffer.Length);
                    entry = new ZipEntry(Path.Combine(parentFolderName, Path.GetFileName(folderToZip) + "/" + Path.GetFileName(file)))
                    {
                        DateTime = DateTime.Now,
                        Size = fs.Length
                    };
                    fs.Close();
                    crc.Reset();
                    crc.Update(buffer);
                    entry.Crc = crc.Value;
                    s.PutNextEntry(entry);
                    s.Write(buffer, 0, buffer.Length);
                }
            }
            catch
            {
                res = false;
            }
            finally
            {
                GC.Collect();
                GC.Collect(1);
            }
            var folders = Directory.GetDirectories(folderToZip);
            return !folders.Any(folder => folderToZip != null && !ZipFileDictory(folder, s, Path.Combine(parentFolderName, Path.GetFileName(folderToZip)))) && res;
        }

        /// <summary>
        /// 压缩目录
        /// </summary>
        /// <param name="folderToZip">待压缩的文件夹，全路径格式</param>
        /// <param name="zipedFile">压缩后的文件名，全路径格式</param>
        /// <param name="level"></param>
        private static bool ZipFileDictory(string folderToZip, string zipedFile, int level)
        {
            if (!Directory.Exists(folderToZip))
            {
                return false;
            }
            var s = new ZipOutputStream(File.Create(zipedFile));
            s.SetLevel(level);
            var res = ZipFileDictory(folderToZip, s, "");
            s.Finish();
            s.Close();
            return res;
        }

        /// <summary>
        /// 压缩文件
        /// </summary>
        /// <param name="fileToZip">要进行压缩的文件名</param>
        /// <param name="zipedFile">压缩后生成的压缩文件名</param>
        /// <param name="level"></param>
        private static bool ZipFile(string fileToZip, string zipedFile, int level)
        {
            if (!File.Exists(fileToZip))
            {
                throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
            }
            FileStream zipFile = null;
            ZipOutputStream zipStream = null;
            ZipEntry zipEntry = null;
            var res = true;
            try
            {
                zipFile = File.OpenRead(fileToZip);
                var buffer = new byte[zipFile.Length];
                zipFile.Read(buffer, 0, buffer.Length);
                zipFile.Close();

                zipFile = File.Create(zipedFile);
                zipStream = new ZipOutputStream(zipFile);
                zipEntry = new ZipEntry(Path.GetFileName(fileToZip));
                zipStream.PutNextEntry(zipEntry);
                zipStream.SetLevel(level);

                zipStream.Write(buffer, 0, buffer.Length);
            }
            catch
            {
                res = false;
            }
            finally
            {
                if (zipEntry != null)
                {
                }
                if (zipStream != null)
                {
                    zipStream.Finish();
                    zipStream.Close();
                }
                GC.Collect();
                GC.Collect(1);
            }
            return res;
        }
        #endregion

        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="fileToZip">待压缩的文件目录</param>
        /// <param name="zipedFile">生成的目标文件</param>
        /// <param name="level">6</param>
        public static bool Zip(String fileToZip, String zipedFile, int level)
        {
            if (Directory.Exists(fileToZip))
            {
                return ZipFileDictory(fileToZip, zipedFile, level);
            }
            return File.Exists(fileToZip) && ZipFile(fileToZip, zipedFile, level);
        }

        /// <summary>
        /// 解压
        /// </summary>
        /// <param name="fileToUpZip">待解压的文件</param>
        /// <param name="zipedFolder">解压目标存放目录</param>
        public static void UnZip(string fileToUpZip, string zipedFolder)
        {
            if (!File.Exists(fileToUpZip))
            {
                return;
            }
            if (!Directory.Exists(zipedFolder))
            {
                Directory.CreateDirectory(zipedFolder);
            }
            ZipInputStream s = null;
            ZipEntry theEntry = null;
            FileStream streamWriter = null;
            try
            {
                s = new ZipInputStream(File.OpenRead(fileToUpZip));
                while ((theEntry = s.GetNextEntry()) != null)
                {
                    if (theEntry.Name == String.Empty) continue;
                    var fileName = Path.Combine(zipedFolder, theEntry.Name);
                    if (fileName.EndsWith("/") || fileName.EndsWith("\\"))
                    {
                        Directory.CreateDirectory(fileName);
                        continue;
                    }
                    streamWriter = File.Create(fileName);
                    var data = new byte[2048];
                    while (true)
                    {
                        var size = s.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            streamWriter.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            finally
            {
                if (streamWriter != null)
                {
                    streamWriter.Close();
                }
                if (theEntry != null)
                {
                }
                if (s != null)
                {
                    s.Close();
                }
                GC.Collect();
                GC.Collect(1);
            }
        }
    }

    public class ZipHelper
    {
        #region 私有变量
        String _theRar;
        RegistryKey _theReg;
        Object _theObj;
        String _theInfo;
        ProcessStartInfo _theStartInfo;
        Process _theProcess;
        #endregion

        /// <summary>
        /// 压缩
        /// </summary>
        /// <param name="zipname">要解压的文件名</param>
        /// <param name="zippath">要压缩的文件目录</param>
        /// <param name="dirpath">初始目录</param>
        public void EnZip(string zipname, string zippath, string dirpath)
        {
            try
            {
                _theReg = Registry.ClassesRoot.OpenSubKey(@"Applications\WinRAR.exe\Shell\Open\Command");
                if (_theReg != null)
                {
                    _theObj = _theReg.GetValue("");
                    _theRar = _theObj.ToString();
                    _theReg.Close();
                }
                _theRar = _theRar.Substring(1, _theRar.Length - 7);
                _theInfo = " a    " + zipname + "  " + zippath;
                _theStartInfo = new ProcessStartInfo
                {
                    FileName = _theRar,
                    Arguments = _theInfo,
                    WindowStyle = ProcessWindowStyle.Hidden,
                    WorkingDirectory = dirpath
                };
                _theProcess = new Process { StartInfo = _theStartInfo };
                _theProcess.Start();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        /// <summary>
        /// 解压缩
        /// </summary>
        /// <param name="zipname">要解压的文件名</param>
        /// <param name="zippath">要解压的文件路径</param>
        public void DeZip(string zipname, string zippath)
        {
            try
            {
                _theReg = Registry.ClassesRoot.OpenSubKey(@"Applications\WinRar.exe\Shell\Open\Command");
                if (_theReg != null)
                {
                    _theObj = _theReg.GetValue("");
                    _theRar = _theObj.ToString();
                    _theReg.Close();
                }
                _theRar = _theRar.Substring(1, _theRar.Length - 7);
                _theInfo = " X " + zipname + " " + zippath;
                _theStartInfo = new ProcessStartInfo
                {
                    FileName = _theRar,
                    Arguments = _theInfo,
                    WindowStyle = ProcessWindowStyle.Hidden
                };
                _theProcess = new Process { StartInfo = _theStartInfo };
                _theProcess.Start();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
    }
}
